<?php

namespace Yolo\Components\EventDispatcher;

use Exception;
use ReflectionException;
use Yolo\ComponentManager\AbstractComponent;
use Yolo\ComponentManager\ComponentManager;
use Yolo\Components\RpcClient\ClientInterface;
use Yolo\Components\RpcClient\ClientManager;
use Yolo\Config\Annotations\UseConfig;
use Yolo\Di\Annotations\Singleton;
use Yolo\Di\DI;
use Yolo\Di\Errors\CircularDependencyException;
use Yolo\Di\Errors\InvalidAttributeException;
use Yolo\Di\Errors\ParameterTypeEmptyException;
use Yolo\Errors\ConfigInvalidException;
use Yolo\Observers\ObserverHelper;
use Yolo\Service\ApplicationContainer;
use Yolo\Utils\Console;

#[Singleton]
class EventDispatcherComponent extends AbstractComponent
{
    protected array $events = [];

    private ?ClientInterface $client;

    public function __construct(
        private readonly ApplicationContainer $container,
        #[UseConfig('rpc.server.instance')]
        private readonly array  $config,
    ){}

    /**
     * Register the component.
     * @return void
     * @throws CircularDependencyException
     * @throws InvalidAttributeException
     * @throws ParameterTypeEmptyException
     * @throws ReflectionException
     * @throws ConfigInvalidException
     */
    public function register(): void
    {
        $manager = DI::use(ClientManager::class);
        $this->client = $manager->getClient('EventCollector');
        $observerHelper = DI::use(ObserverHelper::class);
        $observerHelper->subscribe(
            AfterWorkerStartedObserver::class
        );
    }

    public function addEvent(
        string $type,
        string $topic,
        string $endpoint,
        array $call
    ): void
    {
        if (isset($this->events[$type][$topic])) {
            Console::log("$type $topic already exists, skipped.", 'warning');
            return;
        }

        $this->events[$type][$topic] = [
            'endpoint' => $endpoint,
            'instance' => $this->config,
            'call' => $call,
        ];
    }

    /**
     * @return void
     * @throws CircularDependencyException
     * @throws InvalidAttributeException
     * @throws ParameterTypeEmptyException
     * @throws ReflectionException
     */
    public function listens(): void
    {
        $componentManager = DI::use(ComponentManager::class);
        if (!$componentManager->componentEnabled(EventDispatcherComponent::class)) {
            Console::log("EventDispatcher Component is not enabled.", 'warning');
            return;
        }

        foreach ($this->events as $type => $events) {

            foreach ($events as $topic => $data) {

                try {
                    $this->client->invoke('Register', [
                        'body' => [
                            'instanceId' => $this->container->getConfig()->getInstanceId(),
                            'service' => $this->container->getAppScheme()->getServiceName(),
                            'instance' => $this->config,
                            'endpoint' => $data['endpoint'],
                            'call' => $data['call'],
                            'type' => $type,
                            'topic' => $topic,
                        ]
                    ]);

                    Console::log("EventDispatcher: $type $topic registered on {$data['endpoint']} level.");
                } catch (Exception $e) {
                    Console::log("EventDispatcher: $type $topic failed to register on {$data['endpoint']} level. Reason: " . $e->getMessage(), 'error');
                }
            }
        }
    }

    public function getEvents(): array
    {
        return $this->events;
    }
}
